package cn.hinglo.common.handler;

import cn.hinglo.common.cache.SelfCacheKit;
import cn.hinglo.common.constant.SystemConstant;
import cn.hinglo.common.entity.Result;
import cn.hinglo.common.util.ResultKit;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.jfinal.handler.Handler;
import com.jfinal.kit.HttpKit;
import com.jfinal.kit.JsonKit;
import com.jfinal.kit.Kv;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
import lombok.SneakyThrows;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;

/**
 * @author HingLo
 * @createTime: 2020/9/7 21:15
 * @description: 接口拦截器的
 */
public class ApiHandler extends Handler {

    /***
     * API前缀，用于对该前缀的信息进行拦截
     */
    private String preUrl;
    /***
     * API 信息缓存名称
     */
    private static String URL_CACHE_NAME = "urlCacheName";

    public ApiHandler(String preUrl) {
        this.preUrl = preUrl;
    }

    @Override
    public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
        if (StrUtil.startWith(target, preUrl)) {
            String apiName = request.getParameter(SystemConstant.API_NAME);
            Record record = new Record();
            if (StrUtil.isBlank(apiName)) {
                record = getRequestApi(target);
            } else {
                record.setColumns(this.getKv(request));
                record.set("type", Integer.valueOf(record.getStr("type")));
            }
            if (record != null) {
                // 权限校验
                if (!this.hasPermission(request, response)) {
                    this.printWriter(response, ResultKit.error("无权限访问该资源"));
                } else {
                    String sql = getRealSql(record.getStr("sql"));
                    Integer type = record.getInt("type");
                    String method = record.getStr("method");
                    String method1 = request.getMethod();
                    try {
                        if (!method.equals(method1)) {
                            this.printWriter(response, ResultKit.error(405, "请求方法不支持,请联系管理员"));
                        } else if ("GET".equals(method)) {
                            this.getMethod(request, response, sql, type);
                        } else if ("POST".equals(method)) {
                            this.postMethod(request, response, sql);
                        } else if ("PUT".equals(method)) {
                            this.putMethod(request, response, sql);
                        } else if ("DELETE".equals(method)) {
                            this.deleteMethod(request, response, sql);
                        }
                    } catch (Exception e) {
                        this.printWriter(response, ResultKit.error(e.getMessage()));
                    }
                }
                // 结束直接调用，防止多次调用响应流
                isHandled[0] = true;
            } else {
                next.handle(target, request, response, isHandled);
            }
        } else {
            next.handle(target, request, response, isHandled);
        }
    }

    /***
     * 通过URL获取 该API相关信息
     * @param url 请求的URL
     * @return 返回结果
     */
    private Record getRequestApi(String url) {
        // 移除前缀
        url = StrUtil.removePrefix(url, this.preUrl);
        Record record = SelfCacheKit.get(URL_CACHE_NAME, url);
        if (record == null) {
            record = Db.findFirst("select * from core_api where state=2 and url=?", url);
            if (record != null) {
                SelfCacheKit.put(URL_CACHE_NAME, url, record);
            }
        }
        return record;
    }

    /***
     * 获取所有参数
     * @param request 请求对象
     * @return 返回结果
     */
    private Kv getKv(HttpServletRequest request) {
        Kv kv = new Kv();
        Map<String, String[]> paraMap = request.getParameterMap();
        for (Map.Entry<String, String[]> entry : paraMap.entrySet()) {
            String[] values = entry.getValue();
            String value = (values != null && values.length > 0) ? values[0] : null;
            kv.put(entry.getKey(), "".equals(value) ? null : value);
        }
        return kv;
    }

    /***
     * 响应json 数据
     * @param response 响应对象
     */
    @SneakyThrows
    private void printWriter(HttpServletResponse response, Result<Object> result) {
        response.setStatus(HttpServletResponse.SC_OK);
        response.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
        response.setContentType("application/json; charset=UTF-8");
        out.println(JsonKit.toJson(result));
        out.flush();
    }

    /***
     *  权限校验规则方法
     * @param request 请求信息
     * @param response 响应信息
     * @return 是否通过校验
     */
    private boolean hasPermission(HttpServletRequest request, HttpServletResponse response) {


        return true;
    }

    /***
     * GET 请求方式
     * @param request 请求信息
     * @param response 响应对象
     * @param sql sql
     * @param type 返回类型
     */
    private void getMethod(HttpServletRequest request, HttpServletResponse response, String sql, Integer type) {
        if (!StrUtil.startWith(StrUtil.trim(sql), "select")) {
            this.printWriter(response, ResultKit.error("请求方式与执行SQL不匹配"));
            return;
        }
        Kv kv = this.getKv(request);
        List<Record> result = Db.templateByString(sql, kv).find();
        if (result.size() == 0) {
            this.printWriter(response, ResultKit.success(result));
        } else {
            if (type == 0) {
                if (result.size() > 1) {
                    this.printWriter(response, ResultKit.success(result));
                } else {
                    this.printWriter(response, ResultKit.success(result.get(0)));
                }
            } else if (type == 1) {
                if (result.size() == 1) {
                    this.printWriter(response, ResultKit.success(result.get(0)));
                } else {
                    this.printWriter(response, ResultKit.error("返回类型与数据不匹配"));
                }
            } else {
                this.printWriter(response, ResultKit.success(result));
            }
        }
    }

    /***
     * POST 请求方式，其参数使用body 方式
     * @param request 请求信息
     * @param response 响应信息
     * @param sql 执行sql
     */
    private void postMethod(HttpServletRequest request, HttpServletResponse response, String sql) {
        if (!StrUtil.startWith(StrUtil.trim(sql), "insert")) {
            this.printWriter(response, ResultKit.error("请求方式与执行SQL不匹配"));
            return;
        }
        int update = 0;
        String header = request.getHeader("Content-Type");
        if (header.contains("application/json")) {
            String body = HttpKit.readData(request);
            JSONObject jsonObject = JSONUtil.parseObj(body);
            update = Db.templateByString(sql, jsonObject).update();
        } else {
            Kv kv = this.getKv(request);
            update = Db.templateByString(sql, kv).update();
        }
        this.printWriter(response, ResultKit.success(update));
    }

    /***
     * 更新操作
     * @param request 请求信息
     * @param response 响应信息
     * @param sql 执行sql模板
     */
    private void putMethod(HttpServletRequest request, HttpServletResponse response, String sql) {
        if (!StrUtil.startWith(StrUtil.trim(sql), "update")) {
            this.printWriter(response, ResultKit.error("请求方式与执行SQL不匹配"));
            return;
        }
        Kv kv = this.getKv(request);
        int update = Db.templateByString(sql, kv).update();
        this.printWriter(response, ResultKit.success(update));
    }

    /***
     * 删除操作
     * @param request 请求信息
     * @param response 响应信息
     * @param sql 执行sql模板
     */
    private void deleteMethod(HttpServletRequest request, HttpServletResponse response, String sql) {
        if (!StrUtil.startWith(StrUtil.trim(sql), "delete")) {
            this.printWriter(response, ResultKit.error("请求方式与执行SQL不匹配"));
            return;
        }
        Kv kv = this.getKv(request);
        int delete = Db.templateByString(sql, kv).delete();
        this.printWriter(response, ResultKit.success(delete));
    }


    /***
     * 获取真实的sql，去掉了注释
     * @param sql sql
     * @return 返回结构
     */
    public static String getRealSql(String sql) {
        String[] split = sql.split("[\r\n]");
        StringBuilder stringBuilder = new StringBuilder();
        for (String s : split) {
            if (StrUtil.isNotBlank(s) && !StrUtil.startWith(s, "--") && !StrUtil.startWith(s, "//")) {
                stringBuilder.append(s).append(" ");
            }
        }
        return stringBuilder.toString();
    }

}
